﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text;
using JetBrains.Annotations;
#if USING_LITJSON
using LitJson;
#endif
#if USING_UNITY
using UnityEngine;
#endif
namespace com.susi.util {
    public static class ReflectionUtil {
        public static string[] GetAllMembersName(this Type type, MemberTypes flags)
        {
            var members = type.GetMembers();
            var targets = from member in members
                          where (flags & member.MemberType) == 0
                          select member.Name;
            return targets.ToArray();
        }

        public static IEnumerable<string> GetFieldOrPerotyNames(object ins)
        {
            var type    = ins.GetType();
            var members = type.GetMembers();
            var targets = from member in members
                          where (MemberTypes.Field | MemberTypes.Property).HasFlag(member.MemberType)
                          select member.Name;
            return targets;
        }

        public static string ToFieldOrPropertyMembersString(Type type, object self)
        {
            var tarFlag = MemberTypes.Field | MemberTypes.Property;
            var members = type.GetMembers();
            var fieldsOrPoperty = from member in members
                                  where tarFlag.HasFlag(member.MemberType)
                                  select member;

            var sb = new StringBuilder();
            foreach (var member in fieldsOrPoperty) {
                var    name  = member.Name;
                object value = null;
                switch (member.MemberType) {
                    case MemberTypes.Field:
                        value = type.GetField(name)?.GetValue(self);
                        break;
                    case MemberTypes.Property:
                        value = type.GetProperty(name)?.GetValue(self);
                        break;
                    case MemberTypes.Method:
                        var args = type.GetMethod(name)?.GetGenericArguments();
                        var strs = Array.ConvertAll(args, t => t.Name);
                        var _sb  = new StringBuilder();
                        foreach (var s in strs) {
                            _sb.Append(s);
                            _sb.Append(" ");
                        }

                        value = _sb.ToString();
                        break;
                    default:
                        value = "null";
                        break;
                }

                sb.AppendLine($"{name}:{value}");
            }

            var res = sb.ToString().TrimEnd('\r', '\n');
            return res;
        }

        public static bool TryGetFieldOrProperty<T>(object ins, string name, out T res)
        {
            var type = ins.GetType();
            if (type == null) //凭空构造的type
            {
                res = default;
                return false;
            }

            var field = type.GetRuntimeField(name);
            if (field != null) {
                if (!typeof(T).IsAssignableFrom(field.FieldType)) {
                    res = default;
                    return false;
                }

                res = (T) field.GetValue(ins);
                return true;
            }

            var property = type.GetRuntimeProperty(name);
            if (property != null) {
                if (typeof(T).IsAssignableFrom(property.PropertyType)) {
                    res = default;
                    return false;
                }

                res = (T) property.GetValue(ins);
                return true;
            }

            res = default;
            return false;
        }

        [Obsolete("see TrySetFieldOrProperty ExceptionVersion")]
        public static bool TrySetFieldOrProperty<T>(object ins, string name, T value)
        {
            return TrySetFieldOrProperty(ins, name, value, typeof(T));
            // var type = ins.GetType();
            // var field = type.GetRuntimeField(name);
            // if (field != null)
            // {
            //     if (!typeof(T).IsAssignableFrom(field.FieldType))
            //     {
            //         return false;
            //     }
            //     field.SetValue(ins, value);
            //     return true;
            // }
            // else
            // {
            //     var property = type.GetRuntimeProperty(name);
            //     if (property != null)
            //     {
            //         if (typeof(T).IsAssignableFrom(property.PropertyType))
            //         {
            //             return false;
            //         }
            //         property.SetValue(ins, value);
            //         return true;
            //     }
            //     else
            //     {
            //         return false;
            //     }
            // }
        }

        [Obsolete("see TrySetFieldOrProperty ExceptionVersion")]
        public static bool TrySetFieldOrProperty(object ins, string name, object value, Type valuetype)
        {
            if (!valuetype.IsInstanceOfType(value)) return false;
            var type  = ins.GetType();
            var field = type.GetRuntimeField(name);
            if (field != null) {
                if (!valuetype.IsAssignableFrom(field.FieldType)) return false;
                field.SetValue(ins, value);
                return true;
            }

            var property = type.GetRuntimeProperty(name);
            if (property != null) {
                if (valuetype.IsAssignableFrom(property.PropertyType)) return false;
                property.SetValue(ins, value);
                return true;
            }

            return false;
        }

        /// <summary>
        ///     抛异常版
        /// </summary>
        public static bool TrySetFieldOrProperty(object ins, string name, object value)
        {
            var type  = ins.GetType();
            var field = type.GetRuntimeField(name);
            if (field != null) {
                field.SetValue(ins, value);
                return true;
            }

            var property = type.GetRuntimeProperty(name);
            if (property != null) {
                property.SetValue(ins, value);
                return true;
            }

            return false;
        }


        /// <summary>
        ///     比较两类型是否泛型相同；如果两类型完全相等也会返回true，请在调用前检查isGenericType
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool isSameAsGenericType(Type a, Type b)
        {
            return a.Namespace == b.Namespace && a.Name == b.Name;
        }

        public static string FormatedShotTypeName(Type type)
        {
            if (type.Equals(typeof(int)))
                return "int";
            if (type.Equals(typeof(float)))
                return "float";
            if (type.Equals(typeof(string)))
                return "string";
            return type.Name;
        }

        /// <summary>
        ///     0.ins==null,return="null"
        ///     <br>1.返回该对象在Defintion定义的Name</br>
        ///     <br>2.返回该对象"Name"字段值</br>
        ///     <br>3.返回该对象"Name"属性值</br>
        ///     <br>4.返回该对象"string Name()"结果</br>
        ///     <br>5.返回该对象Type.FullName</br>
        /// </summary>
        /// <param name="ins"></param>
        /// <returns></returns>
        public static string GetName(object ins)
        {
            if (ins == null) return "null";
            //如果已经定义，则跳过反射
            //var res=DefintionSpace.Defintion.GetName(ins);
            //if(res!=null)return res;
            var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
            var type  = ins.GetType();
            var name  = "Name";
            var field = type.GetField(name, flags);
            if (field != null) return field.GetValue(ins).ToString();
            var property = type.GetProperty(name, flags);
            if (property != null) return property.GetValue(ins).ToString();
            var method = type.GetMethod(name, flags);
            if (method != null && method.GetParameters().Length == 0) return method.Invoke(ins, null)?.ToString();
            return type.FullName;
        }

        /// <summary>
        ///     清空对象
        /// </summary>
        /// <param name="ins"></param>
        /// <param name="exceptIObjectDefintion">要排除清空的成员</param>
        public static void ClearAllContentInInstanceExcept(object ins, params string[] memberName)
        {
            bool CheckIsBelongToList(MemberInfo member, string[] memberName1)
            {
                if (memberName1 == null || memberName1.Length == 0) return false;

                string GetRealName(MemberInfo member1)
                {
                    var end = ">k__BackingField"; //< isOK > k__BackingField
                    if (member1.MemberType == MemberTypes.Field && member1.Name.EndsWith(end)) return member1.Name.Remove(member1.Name.Length - end.Length, end.Length).Remove(0, 1);
                    return member1.Name;
                }

                return Array.IndexOf(memberName1, GetRealName(member)) != -1;
            }

            var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            var type  = ins.GetType();
            var mems = from member in type.GetMembers(flags)
                       where !CheckIsBelongToList(member, memberName)
                       where member.MemberType == MemberTypes.Field
                       select member;
            var newone = Activator.CreateInstance(type);
            foreach (var m in mems) {
                var field = type.GetField(m.Name, flags);
                field.SetValue(ins, field.GetValue(newone));
            }
        }

        public static bool IsNumbericIns(object ins)
        {
            return IsNumericType(ins.GetType());
        }

        public static bool IsNumericType(this Type dataType)
        {
            if (dataType == null)
                throw new ArgumentNullException("dataType");
            return dataType == typeof(int)
                   || dataType == typeof(double)
                   || dataType == typeof(long)
                   || dataType == typeof(short)
                   || dataType == typeof(float)
                   || dataType == typeof(short)
                   || dataType == typeof(int)
                   || dataType == typeof(long)
                   || dataType == typeof(uint)
                   || dataType == typeof(ushort)
                   || dataType == typeof(uint)
                   || dataType == typeof(ulong)
                   || dataType == typeof(sbyte)
                   || dataType == typeof(float)
                   || dataType == typeof(decimal);
        }

        /// <summary>
        ///     如果对象有此函数则运行；如果函数对不上参数会抛异常
        /// </summary>
        public static bool TryInvokeMethod(object obj, string methodname, object[] args, out dynamic res)
        {
            if (obj == null || methodname.IsNullOrEmpty()) {
                res = null;
                return false;
            }

            var method = obj.GetType().GetMethod(methodname);
            if (method == null) {
                res = null;
                return false;
            }

            res = method.Invoke(obj, args);
            return true;
        }

        /// <summary>
        ///     如果该类型具有标记了 <see cref="InstanceLoaderByString" />的静态函数（参数是str，具有正确的返回类型）或拥有.ctor(string)，则可以通过反射调用该函数创建该对象
        ///     <para>对<see cref="Nullable{T}"/>会特殊处理</para>
        ///     <para>如果没有定义，则用json反序列化；如果json反序列失败，则返回null</para>
        ///     <para> 特殊:如果type是Type类型且不是泛型类型，则返回所有程序集中唯一一个匹配str（type全名）的type，如果存在多个则会抛异常</para>
        ///     <para>没有开启<see langword="USING_LITJSON"/>的时候，不会进行json反序列化</para>
        /// </summary>
        [CanBeNull]
        public static object GetObjectbyString([NotNull] string str, [NotNull] Type type)
        {
            if (!IsNullableTType(type)) {
                var loader = InstanceLoaderByString.CreatLoader(type);
#if USING_LITJSON
                if (loader == null) return JsonMapper.ToObject(str, type);
#endif
                return loader?.Invoke(str);
            }
            else {
                var intype = type.GetGenericArguments()[0];
                return GetObjectbyString(str, intype);
            }
        }

        /// <summary>
        /// 判断类型是否为<see cref="Nullable{T}"/>类型
        /// </summary>
        public static bool IsNullableTType(Type type)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        }

        /// <summary>
        ///     给予类型名字，选出类型，无异常
        ///     <para>支持短TypeName搜索，无法处理复杂类型</para>
        ///     <para>支持通过<see cref="System.Type.ToString()" />搜索，建议使用</para>
        ///     <para>复杂类型需要程序集限定名，AssemblyQualifiedName，由于无法保证不同平台asb信息一致，不建议用 </para>
        /// </summary>
        [SuppressMessage("ReSharper", "PossibleNullReferenceException")]
        public static TypeInf SelectType([CanBeNull] string type)
        {
            if (type.IsNullOrWhiteSpace()) return new TypeInf(typeof(string), v => v);

            #region 内部固定类型识别

            var lowletter = type.ToLowerInvariant();
            switch (lowletter) {
                case "float":    return new TypeInf(typeof(float), v => GetObjectbyString(v, typeof(float)));
                case "int":      return new TypeInf(typeof(int), v => GetObjectbyString(v, typeof(int)));
                case "string":   return new TypeInf(typeof(string), v => GetObjectbyString(v, typeof(string)));
                case "bool":     return new TypeInf(typeof(bool), v => GetObjectbyString(v, typeof(bool)));
                case "double":   return new TypeInf(typeof(double), v => GetObjectbyString(v, typeof(double)));
                
                case "float?":   return new TypeInf(typeof(float?),v => GetObjectbyString(v, typeof(float?)));
                case "int?":      return new TypeInf(typeof(int?), v => GetObjectbyString(v, typeof(int?)));
                case "bool?":     return new TypeInf(typeof(bool?), v => GetObjectbyString(v, typeof(bool?)));
                case "double?":   return new TypeInf(typeof(double?), v => GetObjectbyString(v, typeof(double?))); 
                
                case "int[]":    return new TypeInf(typeof(int[]), v => GetObjectbyString(v, typeof(int[])));
                case "string[]": return new TypeInf(typeof(string[]), v => GetObjectbyString(v, typeof(string[])));
                case "bool[]":   return new TypeInf(typeof(bool[]), v => GetObjectbyString(v, typeof(bool[])));
                case "float[]":  return new TypeInf(typeof(float[]), v => GetObjectbyString(v, typeof(float[])));
                case "double[]": return new TypeInf(typeof(double[]), v => GetObjectbyString(v, typeof(double[])));
#if USING_UNITY
                case "vec2":    return new TypeInf(typeof(Vector2), v => GetObjectbyString(v, typeof(Vector2)));
                case "vec3":    return new TypeInf(typeof(Vector3), v => GetObjectbyString(v, typeof(Vector3)));
                case "vec4":    return new TypeInf(typeof(Vector4), v => GetObjectbyString(v, typeof(Vector4)));
                case "vec2i":   return new TypeInf(typeof(Vector2Int), v => GetObjectbyString(v, typeof(Vector2Int)));
                case "vec3i":   return new TypeInf(typeof(Vector3Int), v => GetObjectbyString(v, typeof(Vector3Int)));
                
                case "vec2?":  return new TypeInf(typeof(Vector2?), v => GetObjectbyString(v, typeof(Vector2?)));
                case "vec3?":  return new TypeInf(typeof(Vector3?), v => GetObjectbyString(v, typeof(Vector3?)));
                case "vec4?":  return new TypeInf(typeof(Vector4?), v => GetObjectbyString(v, typeof(Vector4?)));
                case "vec2i?": return new TypeInf(typeof(Vector2Int?), v => GetObjectbyString(v, typeof(Vector2Int?)));
                case "vec3i?": return new TypeInf(typeof(Vector3Int?), v => GetObjectbyString(v, typeof(Vector3Int?)));
                
                case "vec2[]":  return new TypeInf(typeof(Vector2[]), v => GetObjectbyString(v, typeof(Vector2[])));
                case "vec3[]":  return new TypeInf(typeof(Vector3[]), v => GetObjectbyString(v, typeof(Vector3[])));
                case "vec4[]":  return new TypeInf(typeof(Vector4[]), v => GetObjectbyString(v, typeof(Vector4[])));
                case "vec2i[]": return new TypeInf(typeof(Vector2Int[]), v => GetObjectbyString(v, typeof(Vector2Int[])));
                case "vec3i[]": return new TypeInf(typeof(Vector3Int[]), v => GetObjectbyString(v, typeof(Vector3Int[])));
#endif
                case "type":

                    Type localGetType(string str)
                    {
                        var typeinf = SelectType(str);
                        if (typeinf.IsErr) throw new Exception($"[error][type:{str}] {typeinf.Err.GetDescriptionStrByAttribute()}");
                        return typeinf.Type;
                    }

                    return new TypeInf(typeof(Type), localGetType);
            }

            #endregion

            #region 反射识别类型

            //简单类搜索获取
            if (!type.Any(v => v == '<' || v == '[')) {
                var fullnamematch  = new List<Type>();
                var shortnamematch = new List<Type>();
                foreach (var type11122 in AppDomain.CurrentDomain.GetAssemblies().SelectMany(v => v.GetTypes()))
                    if (type11122.FullName == type)
                        fullnamematch.Add(type11122);
                    else if (type11122.Name == type) shortnamematch.Add(type11122);

                if (fullnamematch.Count == 1)
                    return new TypeInf(fullnamematch[0], v => GetObjectbyString(v, fullnamematch[0]));
                if (fullnamematch.Count > 1) return new TypeInf(TypeInf.ErrType.MulitType);
                if (shortnamematch.Count == 1)
                    return new TypeInf(shortnamematch[0], v => GetObjectbyString(v, shortnamematch[0]));
                if (shortnamematch.Count > 1) return new TypeInf(TypeInf.ErrType.MulitType);
                return new TypeInf(TypeInf.ErrType.NotExist);
            }
            //全名或以上

            var type3495 = Type.GetType(type, null, delegate(Assembly assembly, string s, bool arg3) { return assembly == null ? AppDomain.CurrentDomain.GetAssemblies().SelectMany(v => v.GetTypes()).FirstOrDefault(v => v.FullName == s) : assembly.GetTypes().FirstOrDefault(v => v.FullName == s); }, false);
            return type3495 == null ? new TypeInf(TypeInf.ErrType.NotExist) : new TypeInf(type3495, v => GetObjectbyString(v, type3495));

            #endregion
        }


#if USING_UNITY
        public static bool IsUniVec(object ins)
        {
            return ins is Vector2 || ins is Vector2Int || ins is Vector3 || ins is Vector3Int || ins is Vector4;
        }
#endif

        public readonly struct TypeInf {
            public enum ErrType {
                [Description("OK")]
                OK,

                [Description("程序中没有定义这个类型")]
                NotExist,

                [Description("程序中定义了不止1个同名类型")]
                MulitType
            }

            public           bool                 IsErr => Err != ErrType.OK;
            public readonly  Type                 Type;
            public readonly  ErrType              Err;
            [NotNull] public string               ErrMsg => Err.GetDescriptionStrByAttribute() ?? "(null)";
            public readonly  Func<string, object> InstanceGetter;

            public TypeInf([NotNull] Type type, Func<string, object> instanceGetter)
            {
                Type           = type ?? throw new ArgumentNullException(nameof(type));
                InstanceGetter = instanceGetter ?? throw new ArgumentNullException(nameof(type));
                Err            = ErrType.OK;
            }

            public TypeInf(ErrType err)
            {
                Type           = null;
                Err            = err;
                InstanceGetter = null;
            }
        }
    }
}